#include "types.h"
#include "uthread.h"
#include "signal.h"
#include "user.h"
#include "procqueue.c"

struct uthread uthreads[MAX_UTHREADS];
uint thread;
struct binary_semaphore uthreads_lock;
void print_uthreads(void)
{
	int i;
	for(i=0;i<MAX_UTHREADS&&i<5;i++)
		printf(1,"%d %d %d %d %d\n",uthreads[i].tid,uthreads[i].esp,uthreads[i].ebp,uthreads[i].stack,uthreads[i].state);
}

void print_stack(int n)
{
	int i;
	printf(1,"STACK FOR THREAD:%d\n",n);
	if(n!=999)
	{	for(i=uthreads[n].ebp-4;i>=uthreads[n].esp;i=i-4)
			printf(1,"address:%d %p\n",i,*(int*)i);
	}
	else
	{
	for(i=uthreads[n].esp+80;i>=uthreads[n].esp;i=i-4)
		printf(1,"address:%d %p\n",i,*(int*)i);
	}
	
}
void uthread_init()
{
	int i;
	uthreads[0].state=T_RUNNING;
	uthreads[0].tid=0;
	STORE_ESP(uthreads[0].esp);
	STORE_EBP(uthreads[0].ebp);
	thread=0;
	for(i=1;i<MAX_UTHREADS;i++)
		{
		uthreads[i].state=T_FREE;
		uthreads[i].tid=i;
		}
	binary_semaphore_init(&uthreads_lock,1);
	signal(SIGALRM,uthread_yield);
	alarm(THREAD_QUANTA);
}
 
int  uthread_create(void (*func)(void *), void* value)
{
	int i;
	binary_semaphore_down(&uthreads_lock);
	for(i=0;i<MAX_UTHREADS;i++)
		{
		if(uthreads[i].state==T_FREE)
			{
			uthreads[i].stack=malloc(STACK_SIZE);
			if(uthreads[i].stack<0)
				return -1;	
			uthreads[i].wait_count=0;
			uthreads[i].ebp=(int)uthreads[i].stack+STACK_SIZE;
			LOAD_ESP(uthreads[i].stack+STACK_SIZE);
			PUSH(value);
			PUSH(uthread_exit);
			PUSH(func);
			PUSH(uthreads[i].ebp);
			STORE_ESP(uthreads[i].esp);
			uthreads[i].ebp=uthreads[i].esp;
			LOAD_ESP(uthreads[thread].esp);
			uthreads[i].state=T_RUNNABLE;
			binary_semaphore_up(&uthreads_lock);		
			return uthreads[i].tid;
			}
		}
	binary_semaphore_up(&uthreads_lock);		
	return -1;
}

void uthread_switch(void)
{
	int i=(thread+1)%MAX_UTHREADS;
	while(uthreads[i].state!=T_RUNNABLE)
		i=(i+1)%MAX_UTHREADS;
	STORE_ESP(uthreads[thread].esp);
	STORE_EBP(uthreads[thread].ebp);
	thread=i;
	LOAD_ESP(uthreads[thread].esp);
	LOAD_EBP(uthreads[thread].ebp);
	uthreads[thread].state=T_RUNNING;
	alarm(THREAD_QUANTA);

}

void uthread_exit(void)
{
	alarm(0);
	if(thread!=0)
		free(uthreads[thread].stack);
	uthreads[thread].ebp=0;
	uthreads[thread].esp=0;
	uthreads[thread].stack=0;
	uthreads[thread].state=T_FREE;
	int i;
	for(i=0;i<uthreads[thread].wait_count;i++)
		uthreads[i].state=T_RUNNABLE;
	uthreads[thread].wait_count=0;
	uthread_switch();

}

void uthread_yield(void)
{
	asm("pusha");
	alarm(0);
	if(uthreads[thread].state==T_RUNNING)
		uthreads[thread].state=T_RUNNABLE;
	else
		printf(1,"YIELDED FROM NON RUNNING THREAD %d\n",uthreads[thread].state);
	uthread_switch();
	asm("popa");
}

void uthread_sleep(void)
{
	asm("pusha");
	alarm(0);
	if(uthreads[thread].state==T_RUNNING)
		uthreads[thread].state=T_SLEEPING;
	else
		printf(1,"SLEEP FROM NON RUNNING THREAD %d\n",uthreads[thread].state);
	uthread_switch();
	asm("popa");
}

int uthread_self(void)
{
	return uthreads[thread].tid;
}

int uthread_join(int tid)
{
	if(uthreads[tid].state!=T_FREE)
	{
		uthreads[tid].wait_list[uthreads[tid].wait_count++]=uthread_self();
		uthread_sleep();
		return 0;
	}
	return -1;
}


void binary_semaphore_init(struct binary_semaphore* semaphore, int value)
{
	semaphore->queue = malloc(sizeof(struct uthreadqueue));
	initQueue(semaphore->queue);
	semaphore->numOfWaiting=0;
	if(value == 0)
	  semaphore->flag = sem_locked;		// value = 0 ==> flag = locked
	else
	  semaphore->flag = sem_free;			// value = 1 ==> flag = free
}

void binary_semaphore_down(struct binary_semaphore* semaphore)
{
	uint alarmTicksRemain = getAlarm();
	alarm(0);
	if(semaphore->flag == sem_locked)
	{
	      semaphore->numOfWaiting++;
	      addToQueue(semaphore->queue,&uthreads[thread]);
	      uthread_sleep();
	      alarm(0);
	      alarmTicksRemain = getAlarm();
	}
	semaphore->flag = sem_locked;
	alarm(alarmTicksRemain);
}

void binary_semaphore_up(struct binary_semaphore* semaphore)
{

	uint alarmTicksRemain = getAlarm();
	alarm(0);
	if(semaphore->numOfWaiting>0)
	{
	      semaphore->numOfWaiting--;
	      struct uthread *ut = removeFromQueue(semaphore->queue);
	      ut->state=T_RUNNABLE;
	}
	else
	      semaphore->flag = sem_free;		// release flag only if no one is already in queue!
	alarm(alarmTicksRemain);
}


/*
void binary_semaphore_down(struct binary_semaphore* semaphore)
{
	uint alarmTicksRemain = THREAD_QUANTA;
	alarm(0);
	addToQueue(semaphore->queue,&uthreads[uthread_self()]);
	semaphore->numOfWaiting++;
	while(semaphore->flag == locked && queue_peek(semaphore->queue)!=uthread_self())
	{
	      uthread_sleep();
	      alarm(0);
	}
	removeFromQueue(semaphore->queue);
	alarm(alarmTicksRemain);
}

void binary_semaphore_up(struct binary_semaphore* semaphore)
{
	uint alarmTicksRemain = THREAD_QUANTA;
	alarm(0);
	semaphore->numOfWaiting--;
	if(semaphore->numOfWaiting>0)
	{
	      struct uthread *ut = queue_pick(semaphore->queue);
	      ut->state=T_RUNNABLE;
	}
	else
	      semaphore->flag = sem_free;		// release flag
	alarm(alarmTicksRemain);
}
*/

/*
void binary_semaphore_init(struct binary_semaphore* semaphore, int value)
{
	semaphore->queue = malloc(sizeof(struct uthreadqueue));
	initQueue(semaphore->queue);
	semaphore->val=value;
	semaphore->flag = locked;
}

void binary_semaphore_down(struct binary_semaphore* semaphore)
{	
	while(xchg(&semaphore->flag, 1) != 0)
	{
		semaphore->val--;
		if (semaphore->val < 0) 
			{ 
			    alarm(0);
			    //add this process to S.L;
			    addToQueue(semaphore->queue,&uthreads[thread]);
			    semaphore->flag=0;
			    uthread_sleep();
			}
		else 
		{
			semaphore->flag=0;
		}
	}
	 //queue_peek(semaphore->queue)!=uthread_self();
}

void binary_semaphore_up(struct binary_semaphore* semaphore)
{
	while(xchg(&semaphore->flag, 1) != 0)
	{
		semaphore->val++;
		if (semaphore->val <= 0) 
			{
			    //remove a process P from S.L;
			    struct uthread *ut = removeFromQueue(semaphore->queue);
			    
			    //semaphore->waitingForTurn[ut->tid] = 0;
			    
			    ut->state=T_RUNNABLE;
			}
	}
	semaphore->flag=0;
}
*/

/***********************************************************************************/

/*
int max_number(int *number) {
    int max,i;
    max=0;
    for(i=0;i<THREAD_QUANTA;i++)
      if(number[i]>max)
	max=number[i];
    return max;
}

void binary_semaphore_init(struct binary_semaphore* semaphore, int value){
  if((value != 0) && (value != 1)){
    printf(2,"value is diff form 1 or 0, value:%d\n",value);
    return;
  }
  
  semaphore = malloc(sizeof(struct binary_semaphore));
  int i;
  for(i=0;i<MAX_UTHREADS;i++){
    semaphore->number[i]=0;
  } 
  semaphore->value = value;
}
  
  

void binary_semaphore_down(struct binary_semaphore* semaphore){
  alarm(0);
  semaphore->number[uthread_self()]=max_number(semaphore->number)+1;
  int i;
  int my_tid = uthread_self();
  while(1) {
    for(i=0;i<MAX_UTHREADS;i++) {
      if(semaphore->number[i]!=0 && (semaphore->number[i] < semaphore->number[my_tid])) {
	uthread_yield();	//not setting alarm because yields resets it.
	alarm(0);
	break;
      }
    }
    if(i==MAX_UTHREADS && semaphore->value==1){	//maybe not needed
      break;			
    }
    uthread_yield();
    alarm(0);
  }
  
  semaphore->value=0;
  alarm(THREAD_QUANTA);
}
  
void binary_semaphore_up(struct binary_semaphore* semaphore) {
    alarm(0);
    semaphore->number[uthread_self()]=0;
    semaphore->value=1;
    //alarm(THREAD_QUANTA);
    uthread_yield();
}
*/
